Admin API Reference
This page is a reference: it describes, endpoint by endpoint, the APIs that the TideCloak admin implementation (the tide-console SPA and the admin operations it performs) calls. It is information-oriented and code-determined. It does not teach concepts or walk through procedures.
- For what the console is and why it works the way it does, see the Admin Overview.
- For procedures (first deploy, IGA, licensing, IdP, E2EE, offboarding), see the Quickstart, Set up IGA, License, Identity Provider, E2EE, and Ragnarok guides.
- For the broad Keycloak-compatible admin surface and governance response codes from a task angle, see the Management API. For authentication endpoints, see the Authentication API.
Conventions
{realm}is the realm path segment.{id}and similarly named placeholders are entity identifiers.- Base prefixes used below:
/admin/realms/{realm}/...is the Keycloak admin REST base (admin token required)./admin/realms/{realm}/vendorResources/...,/admin/realms/{realm}/iga/...,/admin/realms/{realm}/ragnarok/...,/admin/realms/{realm}/tide-admin/..., and/admin/realms/{realm}/tideAdminResources/...are Tide-specific admin resources mounted on the admin REST base./realms/{realm}/public/...and/realms/{realm}/tide-console/...are public, pre-authentication resources.
- Auth column meanings:
manage-realm= an admin access token whose realm-management permissions satisfyrequireManageRealm().view client=requireView(client)on the named client.public= no token (pre-authentication).
- Governed: whether the call is subject to IGA capture. A governed write may return
202 Acceptedwith a change-request id instead of applying immediately. See IGA governance / change requests and the Management API governance section. - File references point at the
@Path/@GET/@POSTannotations in source that this page was written from, so each entry can be verified against the code.
1. Realm and admin core (Keycloak admin REST)
The SPA performs most realm, user, client, role, group, client-scope, organization, identity-provider, session, and admin-event operations through the standard Keycloak Admin REST API. These endpoints are not Tide-specific and are documented in full upstream:
- Keycloak Admin REST API reference: https://www.keycloak.org/docs-api/latest/rest-api/index.html
The api-client (tidecloak-key-provider/frontend/packages/api-client/src/keycloak/*) calls the following standard endpoints. Method + path + one-line purpose only; consult the upstream reference for parameters and response schemas. All require an admin token (manage-realm/view-realm or the corresponding resource permission).
Realms, server info, permissions
| Method | Path | Purpose |
|---|---|---|
| GET | /admin/realms | List realms. |
| POST | /admin/realms | Create a realm. |
| GET | /admin/realms/{realm} | Get realm representation. |
| PUT | /admin/realms/{realm} | Update realm. |
| GET | /admin/realms/{realm}/admin-events | List admin audit events. |
| GET | /admin/serverinfo | Server version / capabilities. |
| GET | /admin/realms/{realm}/users-management-permissions | Probe fine-grained admin feature. |
| GET | /admin/realms/{realm}/{resource}/{id}/management/permissions | Get resource (role/group/client) permissions. |
| PUT | /admin/realms/{realm}/{resource}/{id}/management/permissions | Enable/disable resource permissions. |
| GET | /admin/realms/{realm}/components | Look up a component (e.g. key provider) by providerId. |
Identity providers and required actions
| Method | Path | Purpose |
|---|---|---|
| GET | /admin/realms/{realm}/identity-provider/instances | List identity providers. |
| GET | /admin/realms/{realm}/identity-provider/instances/{alias} | Get an identity provider. |
| PUT | /admin/realms/{realm}/identity-provider/instances/{alias} | Update an identity provider. |
| DELETE | /admin/realms/{realm}/identity-provider/instances/{alias} | Delete an identity provider. |
| GET | /admin/realms/{realm}/authentication/required-actions | List required actions. |
| POST | /admin/realms/{realm}/authentication/register-required-action | Register a required-action provider. |
Users
| Method | Path | Purpose |
|---|---|---|
| GET | /admin/realms/{realm}/users | List / search users. |
| GET | /admin/realms/{realm}/users/count | Count users. |
| GET | /admin/realms/{realm}/users/{id} | Get a user. |
| POST | /admin/realms/{realm}/users | Create a user. |
| PUT | /admin/realms/{realm}/users/{id} | Update a user. |
| DELETE | /admin/realms/{realm}/users/{id} | Delete a user. |
| GET | /admin/realms/{realm}/users/{id}/credentials | List user credentials. |
| POST | /admin/realms/{realm}/users/{id}/credentials/{credentialId}/userLabel | Set a credential label. |
| DELETE | /admin/realms/{realm}/users/{id}/credentials/{credentialId} | Remove a credential. |
| GET | /admin/realms/{realm}/users/{id}/groups | List a user's groups. |
| POST | /admin/realms/{realm}/users/{id}/groups/{groupId} | Add user to group. |
| DELETE | /admin/realms/{realm}/users/{id}/groups/{groupId} | Remove user from group. |
| GET | /admin/realms/{realm}/users/{id}/role-mappings | List all role mappings. |
| GET | /admin/realms/{realm}/users/{id}/role-mappings/realm | List realm role mappings. |
| GET | /admin/realms/{realm}/users/{id}/role-mappings/realm/available | List available realm roles. |
| POST | /admin/realms/{realm}/users/{id}/role-mappings/realm | Assign realm roles. |
| DELETE | /admin/realms/{realm}/users/{id}/role-mappings/realm | Remove realm roles. |
| GET | /admin/realms/{realm}/users/{id}/role-mappings/clients/{clientUuid} | List client role mappings. |
| GET | /admin/realms/{realm}/users/{id}/role-mappings/clients/{clientUuid}/available | List available client roles. |
| POST | /admin/realms/{realm}/users/{id}/role-mappings/clients/{clientUuid} | Assign client roles. |
| DELETE | /admin/realms/{realm}/users/{id}/role-mappings/clients/{clientUuid} | Remove client roles. |
| GET | /admin/realms/{realm}/users/{id}/federated-identity | List federated identities. |
| DELETE | /admin/realms/{realm}/users/{id}/federated-identity/{provider} | Remove a federated link. |
| GET | /admin/realms/{realm}/users/{id}/user-sessions | List user sessions. |
| GET | /admin/realms/{realm}/users/{id}/consents | List user consents. |
Groups
| Method | Path | Purpose |
|---|---|---|
| GET | /admin/realms/{realm}/groups | List groups. |
| GET | /admin/realms/{realm}/groups/count | Count groups. |
| GET | /admin/realms/{realm}/groups/{id} | Get a group. |
| GET | /admin/realms/{realm}/groups/{id}/children | List child groups. |
| POST | /admin/realms/{realm}/groups | Create a root group. |
| POST | /admin/realms/{realm}/groups/{parentId}/children | Create child group / move group. |
| PUT | /admin/realms/{realm}/groups/{id} | Update a group. |
| DELETE | /admin/realms/{realm}/groups/{id} | Delete a group. |
| GET | /admin/realms/{realm}/groups/{id}/members | List group members. |
| GET | /admin/realms/{realm}/groups/{id}/role-mappings/realm | List group realm roles. |
| GET | /admin/realms/{realm}/groups/{id}/role-mappings/realm/available | List available realm roles. |
| POST | /admin/realms/{realm}/groups/{id}/role-mappings/realm | Add realm roles. |
| DELETE | /admin/realms/{realm}/groups/{id}/role-mappings/realm | Remove realm roles. |
| GET | /admin/realms/{realm}/groups/{id}/role-mappings/clients/{clientUuid} | List group client roles. |
| GET | /admin/realms/{realm}/groups/{id}/role-mappings/clients/{clientUuid}/available | List available client roles. |
| POST | /admin/realms/{realm}/groups/{id}/role-mappings/clients/{clientUuid} | Add client roles. |
| DELETE | /admin/realms/{realm}/groups/{id}/role-mappings/clients/{clientUuid} | Remove client roles. |
Roles
| Method | Path | Purpose |
|---|---|---|
| GET | /admin/realms/{realm}/roles | List realm roles. |
| GET | /admin/realms/{realm}/roles/{roleName} | Get realm role by name. |
| GET | /admin/realms/{realm}/roles-by-id/{roleId} | Get role by id. |
| POST | /admin/realms/{realm}/roles | Create a realm role. |
| PUT | /admin/realms/{realm}/roles-by-id/{roleId} | Update a role. |
| DELETE | /admin/realms/{realm}/roles-by-id/{roleId} | Delete a role. |
| GET | /admin/realms/{realm}/roles-by-id/{roleId}/composites | List composite children. |
| POST | /admin/realms/{realm}/roles-by-id/{roleId}/composites | Add composite children. |
| DELETE | /admin/realms/{realm}/roles-by-id/{roleId}/composites | Remove composite children. |
| GET | /admin/realms/{realm}/roles/{roleName}/users | List users holding a role. |
Clients and client scopes
| Method | Path | Purpose |
|---|---|---|
| GET | /admin/realms/{realm}/clients | List clients. |
| GET | /admin/realms/{realm}/clients/{id} | Get a client. |
| POST | /admin/realms/{realm}/clients | Create a client. |
| PUT | /admin/realms/{realm}/clients/{id} | Update a client. |
| DELETE | /admin/realms/{realm}/clients/{id} | Delete a client. |
| GET | /admin/realms/{realm}/clients/{id}/client-secret | Get client secret. |
| POST | /admin/realms/{realm}/clients/{id}/client-secret | Regenerate client secret. |
| GET | /admin/realms/{realm}/clients/{id}/protocol-mappers/models | List protocol mappers. |
| POST | /admin/realms/{realm}/clients/{id}/protocol-mappers/models | Create a protocol mapper. |
| PUT | /admin/realms/{realm}/clients/{id}/protocol-mappers/models/{mapperId} | Update a protocol mapper. |
| DELETE | /admin/realms/{realm}/clients/{id}/protocol-mappers/models/{mapperId} | Delete a protocol mapper. |
| GET | /admin/realms/{realm}/clients/{id}/roles | List client roles. |
| GET | /admin/realms/{realm}/clients/{id}/user-sessions | List client user sessions. |
| GET | /admin/realms/{realm}/clients/{id}/default-client-scopes | List default client scopes. |
| PUT | /admin/realms/{realm}/clients/{id}/default-client-scopes/{scopeId} | Add a default client scope. |
| DELETE | /admin/realms/{realm}/clients/{id}/default-client-scopes/{scopeId} | Remove a default client scope. |
| GET | /admin/realms/{realm}/clients/{id}/optional-client-scopes | List optional client scopes. |
| PUT | /admin/realms/{realm}/clients/{id}/optional-client-scopes/{scopeId} | Add an optional client scope. |
| DELETE | /admin/realms/{realm}/clients/{id}/optional-client-scopes/{scopeId} | Remove an optional client scope. |
| GET | /admin/realms/{realm}/clients/{id}/authz/resource-server | Get authorization config. |
| PUT | /admin/realms/{realm}/clients/{id}/authz/resource-server | Update authorization config. |
| GET | /admin/realms/{realm}/clients/{id}/service-account-user | Get the service-account user. |
| GET | /admin/realms/{realm}/client-scopes | List client scopes. |
Sessions and organizations
| Method | Path | Purpose |
|---|---|---|
| DELETE | /admin/realms/{realm}/sessions/{sessionId} | Terminate a user session. |
| GET | /admin/realms/{realm}/organizations | List organizations. |
| GET | /admin/realms/{realm}/organizations/count | Count organizations. |
| GET | /admin/realms/{realm}/organizations/{id} | Get an organization. |
| POST | /admin/realms/{realm}/organizations | Create an organization. |
| PUT | /admin/realms/{realm}/organizations/{id} | Update an organization. |
| DELETE | /admin/realms/{realm}/organizations/{id} | Delete an organization. |
| GET | /admin/realms/{realm}/ui-ext/info | UI-extension info. |
When IGA is on for a non-
masterrealm, the governed subset of these writes is captured into change requests rather than applied directly. See section 4.
2. Tide vendor, realm setup, settings, and branding (VendorResource)
Mounted by VendorResourceProvider (provider id vendorResources) at /admin/realms/{realm}/vendorResources/.... Every endpoint here requires manage-realm unless noted. Source: tidecloak-idp-extensions/tidecloak-key-provider/src/main/java/org/tidecloak/tidecustom/VendorResource.java.
POST setUpTideRealm
- Path:
POST /admin/realms/{realm}/vendorResources/setUpTideRealm(VendorResource.java:165) - Purpose: Provisions the Tide IdP and vendor-key component on the realm, the
tide-claimsscope andt.uhomapper, unmanaged attributes, and (unless skipped) free-tier licensing. - Auth:
manage-realm. - Request (form):
email(string, free-tier contact),isRagnarokEnabled(boolean, default true),skipLicense(boolean, default false; when true skips Stripe setup). - Response:
200text/plain, JSON marker such as{"status":"idp-created"}or a free-tier licensing result. - Governed: No. Sets the
IGA_VENDOR_PROVISIONINGsession flag so nested realm-config writes are exempt from IGA capture.
POST generate-initial-key
- Path:
POST /admin/realms/{realm}/vendorResources/generate-initial-key(VendorResource.java:298) - Purpose: Mints the realm's VVK, gVRK, gVRK certificate, and FirstAdmin authorizer via Midgard, completing key provisioning.
- Auth:
manage-realm. - Request: empty form body.
- Response:
200text/plain("Key successfully generated"); on ORK signing/threshold failure throws aTideErrorException(codeVENDOR_KEYGEN_FAILED,500) after tearing down the vendor-key component. - Governed: No (sets
IGA_VENDOR_PROVISIONING).
POST sign-idp-settings
- Path:
POST /admin/realms/{realm}/vendorResources/sign-idp-settings(VendorResource.java:475) - Purpose: Signs the realm's current Tide IdP settings (registration-allowed, backup, branding, per-client-origin auth) with the active VRK and stores the signatures and signed blob for ORK verification.
- Auth:
manage-realm. - Request: empty body.
- Response:
200text/plain(success);400if no Tide component/IdP;500on signing failure. - Governed: No (sets
IGA_VENDOR_PROVISIONING). Also invoked internally on governance commit whenregistrationAllowedchanges.
POST switch-vrk
- Path:
POST /admin/realms/{realm}/vendorResources/switch-vrk(VendorResource.java:110) - Purpose: Switches the active VRK to a specified generated VRK.
- Auth:
manage-realm. - Request:
gvrk(query param, optional). - Response:
200text/plain;404if the gVRK is not found;500on error.
POST readd-tide-key
- Path:
POST /admin/realms/{realm}/vendorResources/readd-tide-key(VendorResource.java:435) - Purpose: Re-adds the tide-vendor-key component.
- Auth: As of source (VendorResource.java:435), this method does not call
requireManageRealm(), unlike its sibling vendor endpoints. Described here as observed code behavior. - Response:
200text/plain("re-added tide-vendor-key");500on error.
GET get-branding
- Path:
GET /admin/realms/{realm}/vendorResources/get-branding(VendorResource.java:530) - Purpose: Returns the realm's branding URLs.
- Auth:
manage-realm. - Response:
200JSON{ "backgroundUrl": "...", "logoUrl": "..." };400/500on error.
POST set-branding
- Path:
POST /admin/realms/{realm}/vendorResources/set-branding(VendorResource.java:570) - Purpose: Sets the realm's branding URLs (null fields left unchanged).
- Auth:
manage-realm. - Request: JSON
{ "backgroundUrl"?: string, "logoUrl"?: string }. - Response:
200text/plain;400/500on error.
POST tide-idp-admin-resources/images/upload
- Path:
POST /admin/realms/{realm}/tide-idp-admin-resources/images/upload(called from api-clientvendor/index.ts:215) - Purpose: Uploads a branding image; returns a content hash and stored name.
- Auth:
manage-realm. - Request:
multipart/form-datawithfile,fileName,fileType. - Response:
{ "hash": "...", "name": "..." }.
GET get-installations-provider
- Path:
GET /admin/realms/{realm}/vendorResources/get-installations-provider(VendorResource.java:983) - Purpose: Returns a client installation configuration (e.g.
keycloak-oidc-keycloak-json) augmented with Tide fields. - Auth:
viewon the named client. - Request:
clientId(query, client UUID),providerId(query, optional). - Response:
200, content type per provider;500on error.
GET get-tide-jwk
- Path:
GET /admin/realms/{realm}/vendorResources/get-tide-jwk(VendorResource.java:1021) - Purpose: Returns the EdDSA public key as a JWK.
- Auth:
manage-realm. - Response:
200text/plain(JWK JSON);400if no component;500on error.
Scheduled-task endpoints
| Method | Path | Purpose | Source |
|---|---|---|---|
| GET | /admin/realms/{realm}/vendorResources/scheduledTasks | List scheduled tasks (ScheduledTaskInfo[]). | VendorResource.java:860 |
| POST | /admin/realms/{realm}/vendorResources/scheduledTasks/{taskName}/trigger | Trigger a named task; 404 if the task is unknown. | VendorResource.java:895 |
| POST | /admin/realms/{realm}/vendorResources/scheduledTasks/genVRK/schedule | Schedule the VRK generation task. | VendorResource.java:932 |
All three require manage-realm. A read-only GET /realms/{realm}/vendorResources/scheduledTasks (non-admin) is also called by the SPA (vendor/index.ts:374).
3. Licensing (VendorResource Stripe / license endpoints)
Also on VendorResource; all require manage-realm. These surface the Stripe-backed licensing state and checkout flows.
| Method | Path | Purpose | Response | Source |
|---|---|---|---|---|
| GET | /admin/realms/{realm}/vendorResources/getLicenseDetails | License details for the realm. | 200 text/plain (license JSON) or 500. | VendorResource.java:739 |
| GET | /admin/realms/{realm}/vendorResources/getSubscriptionStatus | Stripe subscription status (verbatim string). | 200 text/plain (status); throws TideErrorException VENDOR_SUBSCRIPTION_LOOKUP_FAILED (500) on lookup failure. | VendorResource.java:776 |
| GET | /admin/realms/{realm}/vendorResources/isPendingLicenseActive | Whether a pending license is active. | 200 text/plain boolean string; 400/500. | VendorResource.java:703 |
| GET | /admin/realms/{realm}/vendorResources/licenseHistory | License history. | 200 JSON array. As of source (VendorResource.java:1062) the method returns an empty array: it carries a TODO to rewire onto the IGA licensing-history pipeline (the old IGA's LicenseHistoryAdapter was removed during decoupling). | VendorResource.java:1062 |
| POST | /admin/realms/{realm}/vendorResources/createStripeCheckoutSession | Create a Stripe checkout session for a tier. | 303 text/plain redirect URL. Request (form): licensingTier. | VendorResource.java:241 |
| POST | /admin/realms/{realm}/vendorResources/createCustomerPortalSession | Create a Stripe customer-portal session. | 303 text/plain redirect URL. Request (form): redirectUrl. | VendorResource.java:822 |
| GET | /admin/realms/{realm}/vendorResources/triggerLicenseRenewedEvent/{error} | Emit a license-renewed admin event (test/diagnostic). | 200 text/plain. Path: error (boolean). | VendorResource.java:963 |
For the procedure, see the License guide.
4. IGA governance / change requests
Mounted by IgaAdminResourceProviderFactory (provider id iga) with class @Path("iga") at /admin/realms/{realm}/iga/.... Source: tidecloak-iga-extensions/iga-core/src/main/java/org/tidecloak/iga/rest/IgaAdminResource.java. All endpoints require manage-realm unless noted.
These endpoints carry the governed status codes. For the conceptual model (capture, quorum, approver roles, scope mode) see Set up IGA and the Management API governance section. The in-repo tidecloak-iga-extensions/docs/qea-iga-api.md is the developer source for the lifecycle; this page reflects the endpoints the admin implementation actually uses, including ones that doc predates.
Change-request lifecycle
| Method | Path | Purpose | Key responses | Source |
|---|---|---|---|---|
| GET | /iga/change-requests?status={status} | List CRs (default status=PENDING; also APPROVED, DENIED, CANCELLED). | 200 array of CR representations. | IgaAdminResource.java:294 |
| GET | /iga/change-requests/{id} | Fetch a single CR. | 200; 404 if unknown or in another realm. | IgaAdminResource.java:391 |
| POST | /iga/change-requests/{id}/approve | Unified admin-UI lane: records the caller's approval and auto-commits when quorum is reached. For multiAdmin it runs two-phase: phase 1 (no body) returns a requestModel to sign in the enclave; phase 2 ({"requestModel": "..."}) submits the signed carrier. | 200 (shape varies by mode: recorded with committed/authCount/threshold, or needs-approval with requestModel); governed codes below; 409 NOT_MULTI_ADMIN / NOT_TIDE_ATTESTOR; 400 APPROVAL_MODEL_INVALID. | IgaAdminResource.java:1093 |
| POST | /iga/change-requests/{id}/commit | Apply-only: commits a CR that has already reached quorum (replay and apply). Does not record a new approval. | 200 {committed,changeRequestId,status,changeRequest}; governed codes below. | IgaAdminResource.java:623 |
| POST | /iga/change-requests/{id}/authorize | Legacy single-phase authorize (records one approval, auto-commits at quorum). Refuses multiAdmin realms. Retained for internal/bulk reuse. | 200; 403 FORBIDDEN_APPROVER_ROLE; 409 (not PENDING / already signed / MULTIADMIN_REQUIRES_APPROVAL_ENCLAVE). | IgaAdminResource.java:529 |
| POST | /iga/change-requests/{id}/deny | Deny a CR. | 204 No Content; 404. | IgaAdminResource.java:1781 |
| POST | /iga/change-requests/bulk-authorize | Drain many CRs in one call (typically the toggle-on ADOPT set). | 200 {results[], summary} (read per-CR status); 400 on bad input; 429 if a bulk run already holds the per-realm lock. | IgaAdminResource.java:1334 |
| PUT | /iga/change-requests/{id} | Replace the CR's rows; deletes all authorizations (re-signing required). | 200; 400 if rows missing; 404. Body {"rows":[...]}. | IgaAdminResource.java:1749 |
| POST | /iga/adopt | Create a manual ADOPT change request for an entity. | 201 {changeRequestId,entityType,entityId}; 400; 409 ALREADY_ATTESTED. Body {entityType,entityId}. | IgaAdminResource.java:1807 |
Two-phase approval (multiAdmin, legacy split)
Preserved for back-compat; the unified /approve endpoint above is what current admin UIs call.
| Method | Path | Purpose | Source |
|---|---|---|---|
| GET | /iga/change-requests/{id}/approval-model | Phase 1: build the enclave requestModel to sign. Returns 409 NOT_MULTI_ADMIN/NOT_TIDE_ATTESTOR off-mode. | IgaAdminResource.java:913 |
| POST | /iga/change-requests/{id}/approval-model | Phase 2: submit the signed requestModel; records approval and returns quorum status (does not auto-commit). | IgaAdminResource.java:972 |
Diagnostics, preview, and comments
| Method | Path | Purpose | Source |
|---|---|---|---|
| GET | /iga/change-requests/{id}/diagnostic-bundle | Read-only diagnostic snapshot of a CR (diag_kind: iga_cr_bundle, rows, authorizations, threshold, approver role). | IgaAdminResource.java:431 |
| POST | /iga/change-requests/{id}/first-admin-sign-preview | Denormalized signing-payload preview (no cryptography). | IgaAdminResource.java:1732 |
| GET | /iga/change-requests/{id}/comments | List comments. | IgaAdminResource.java:1859 |
| POST | /iga/change-requests/{id}/comments | Add a comment ({comment} or {body}, max 2000 chars; 201). | IgaAdminResource.java:1878 |
| PUT | /iga/change-requests/{id}/comments/{commentId} | Edit a comment (author only; 403 otherwise). | IgaAdminResource.java:1920 |
| DELETE | /iga/change-requests/{id}/comments/{commentId} | Delete a comment (author or manage-realm; 204). | IgaAdminResource.java:1965 |
Governed status codes
| Status | Meaning / error code |
|---|---|
200 OK | List / get, or an approve / commit that succeeded. |
201 Created | ADOPT created. |
202 Accepted | A governed admin write was captured into a CR (returned by the captured write itself, not by these lifecycle endpoints). Location points at the CR. |
204 No Content | Deny, comment delete, or a policy delete. |
400 Bad Request | APPROVAL_MODEL_INVALID; missing rows; bad bulk input; oversized comment. |
403 Forbidden | FORBIDDEN_APPROVER_ROLE (caller lacks the required approver role); non-author comment edit; reserved policy name. |
404 Not Found | Unknown CR id; ENTITY_VANISHED on a commit whose ADOPT target was deleted out of band. |
409 Conflict | CR not PENDING; same admin already signed; MULTIADMIN_REQUIRES_APPROVAL_ENCLAVE (legacy authorize lane refusing a multiAdmin CR); NOT_MULTI_ADMIN / NOT_TIDE_ATTESTOR (two-phase endpoints off-mode); ALREADY_ATTESTED (adopt); MAPPER_CLAIM_CONFLICT (replay would duplicate a protocol-mapper claim). |
412 Precondition Failed | Commit gate not met: QUORUM_NOT_MET (multiAdmin commit below quorum), DEPENDENCY_NOT_MET (prerequisite CR not approved), PENDING_ADMIN_GRANTS (REGEN_ADMIN_POLICY committing while covered admin grants are still pending). |
429 Too Many Requests | A bulk-authorize is already running for this realm. |
Realm / role policies
Threshold-and-approver policy records. Reads are available to any authenticated admin; writes require manage-realm. The reserved name tide-realm-admin is read-only (writes return 403).
| Method | Path | Purpose | Source |
|---|---|---|---|
| GET | /iga/role-policies | List named policies. | IgaAdminResource.java:2110 |
| GET | /iga/role-policies/{id} | Get a policy by id. | IgaAdminResource.java:2120 |
| GET | /iga/role-policies/name/{name} | Get a policy by name. | IgaAdminResource.java:2132 |
| POST | /iga/role-policies | Upsert a policy. | IgaAdminResource.java:2145 |
| DELETE | /iga/role-policies/name/{name} | Delete a policy by name (403 for the reserved name). | IgaAdminResource.java:2200 |
| DELETE | /iga/role-policies/{id} | Delete a policy by id. | IgaAdminResource.java:2222 |
Authorizers
IGA authorizer records (providerId, type, authorizer, authorizerCertificate). All require manage-realm.
| Method | Path | Purpose | Key responses | Source |
|---|---|---|---|---|
| GET | /iga/authorizers?type={type} | List authorizers, optionally filtered by type. | 200 array. | IgaAdminResource.java:2008 |
| GET | /iga/authorizers/{id} | Get an authorizer by id. | 200; 404 if unknown or in another realm. | IgaAdminResource.java:2026 |
| POST | /iga/authorizers | Create an authorizer. Body requires providerId, type, authorizer, authorizerCertificate. | 201; 400 if the body or any required field is missing. | IgaAdminResource.java:2039 |
| DELETE | /iga/authorizers/{id} | Delete an authorizer. | 204; 404 if unknown or in another realm. | IgaAdminResource.java:2083 |
Forseti contracts
Reusable smart-contract code chunks (keyed by body hash). All require manage-realm.
| Method | Path | Purpose | Key responses | Source |
|---|---|---|---|---|
| GET | /iga/forseti-contracts | List contracts for the realm. | 200 array. | IgaAdminResource.java:2248 |
| GET | /iga/forseti-contracts/{id} | Get a contract by id. | 200; 404 if unknown or in another realm. | IgaAdminResource.java:2259 |
| POST | /iga/forseti-contracts | Upsert a contract (idempotent on body hash). Body requires contractCode (max 1048576 chars), optional name. | 200; 400 if contractCode missing or oversized. | IgaAdminResource.java:2272 |
| DELETE | /iga/forseti-contracts/{id} | Delete a contract by id. | 204; 404 if unknown or in another realm. | IgaAdminResource.java:2302 |
Server certificates
Workload TLS / SPIFFE server-cert request drafts. All require manage-realm.
| Method | Path | Purpose | Key responses | Source |
|---|---|---|---|---|
| GET | /iga/server-certs | List all server-cert drafts. | 200 array. | IgaAdminResource.java:2320 |
| GET | /iga/server-certs/active | List active server-cert drafts. | 200 array. | IgaAdminResource.java:2330 |
| GET | /iga/server-certs/{id} | Get a draft by id. | 200; 404 if unknown or in another realm. | IgaAdminResource.java:2340 |
| GET | /iga/server-certs/instance/{instanceId} | List drafts for a workload instance. | 200 array. | IgaAdminResource.java:2352 |
| POST | /iga/server-certs/request | File a cert request. Body requires clientId, instanceId, publicKey (max 4096 chars); optional spiffeId (max 512), publicKeyFingerprint, requestedLifetime, signedPolicy (max 8192). | 201; 400 on missing/oversized field. | IgaAdminResource.java:2364 |
| POST | /iga/server-certs/{id}/issue | Mark a draft issued. Body requires certificate, trustBundle. | 200; 400 if either missing; 404. | IgaAdminResource.java:2422 |
| POST | /iga/server-certs/{id}/revoke | Revoke an issued cert. | 200; 404. | IgaAdminResource.java:2452 |
| DELETE | /iga/server-certs/{id} | Delete a draft. | 204; 404. | IgaAdminResource.java:2467 |
Licensing drafts and history
Realm license install/rotate draft flow plus the append-only license-history audit log. All require manage-realm.
| Method | Path | Purpose | Key responses | Source |
|---|---|---|---|---|
| POST | /iga/licensing/trigger | Create a licensing draft. Body requires actionType (INSTALL_LICENSE or ROTATE_LICENSE). | 201; 400 if actionType missing or not one of the two values. | IgaAdminResource.java:2485 |
| GET | /iga/licensing/drafts | List licensing drafts. | 200 array. | IgaAdminResource.java:2513 |
| GET | /iga/licensing/drafts/{id} | Get a draft by id. | 200; 404. | IgaAdminResource.java:2523 |
| DELETE | /iga/licensing/drafts/{id} | Delete a draft. | 204; 404. | IgaAdminResource.java:2535 |
| POST | /iga/licensing/drafts/{draftId}/issue | Issue a license against a draft. Body requires providerId, vrk, gvrk, signature; optional gvrkCertificate. | 200; 400 on missing field; 404. | IgaAdminResource.java:2594 |
| GET | /iga/licensing/history | List license-history records. | 200 array. | IgaAdminResource.java:2553 |
| GET | /iga/licensing/history/{id} | Get a history record by id. | 200; 404. | IgaAdminResource.java:2563 |
| GET | /iga/licensing/history/excluding-active?activeGvrk={gvrk} | List history records excluding the one for activeGvrk. | 200 array. | IgaAdminResource.java:2575 |
5. tide-admin (admin-UI governance bridge)
The SPA api-client (tidecloak-key-provider/frontend/packages/api-client/src/tide-admin/index.ts) drives a tide-admin API surface that wraps the IGA change-request lifecycle for the admin UI. Two distinct realities sit behind this client, and the entries below separate them:
- Server-resolved. The provider id
tide-adminis served byTideAdminCompatResource(mounted byTideAdminCompatResourceProviderFactory,@Path("tide-admin")) at/admin/realms/{realm}/tide-admin/.... Source:tidecloak-iga-extensions/iga-core/src/main/java/org/tidecloak/iga/rest/TideAdminCompatResource.java. In the current source tree that class serves exactly three endpoints (toggle-iga,iga-status,toggle-iga/status/{jobId}). - Routed to
iga. Several api-client methods named in thetide-adminmodule call the IGA resource directly under/admin/realms/{realm}/iga/...(see section 4); the realm-policy reads/writes are the clearest case. - Legacy compat, not in this build. The remaining
tide-admin/*paths the api-client references (change-set/*,realm-policy/*,server-cert/*,users/{id}/draft/status, and the per-mapping draft-status probes) have no@Pathin the current Java sources. The api-client treats a404from these as a no-op (empty list /{}/ zero counts) so a build without the legacy surface degrades quietly. They are documented here by their api-client call site as observed behavior; their server resource class is not present in source.
Server-resolved tide-admin endpoints
Mounted on TideAdminCompatResource. All require manage-realm unless noted.
| Method | Path | Purpose | Key responses | Source |
|---|---|---|---|---|
| POST | /tide-admin/toggle-iga | Flip IGA on/off for the realm (next = !current). On OFF to ON (non-master) it provisions the tide-realm-admin approver role, sets iga.attestor=tide and EdDSA (when the VRK is active), runs the ADOPT scan, invalidates sessions, and runs the firstAdmin auto-commit sweep. On ON to OFF (non-master) it captures a governed DISABLE_IGA change request instead of flipping directly. Body (form): isIGAEnabled (informational), optional jobId for progress polling. | 200 {enabled, scan?, autoCommit?, approverRoleRepoint?, warning?}; 202 (ON to OFF, governed DISABLE_IGA CR filed); 409 SIDECAR_CAP_EXCEEDED. | TideAdminCompatResource.java:104 |
| GET | /tide-admin/iga-status | Report whether IGA is enabled for the realm. | 200 {enabled}. Auth: view-realm. | TideAdminCompatResource.java:570 |
| GET | /tide-admin/toggle-iga/status/{jobId} | Poll live progress for a toggle-on job: {jobId, state, currentStageId, stages[], error}. | 200; 404 UNKNOWN_JOB. | TideAdminCompatResource.java:587 |
tide-admin methods routed to the iga resource
These api-client methods sit in the tide-admin module but call /admin/realms/{realm}/iga/.... See Realm / role policies in section 4 for the server entries.
| api-client method | Path called | Purpose | Source (api-client) |
|---|---|---|---|
getChangeSetCounts | GET /iga/change-requests?status=PENDING | Derive {users, roles, clients, groups, total} by bucketing the PENDING CR list on entityType. 404/403 collapse to all-zeros. | tide-admin/index.ts:65 |
listRealmPolicies / listSshPolicies | GET /iga/role-policies | List named realm policies. 404/403 collapse to []. | tide-admin/index.ts:352, :454 |
getRealmPolicyByName | GET /iga/role-policies/name/{name} | Find a policy by name. 404 resolves to null. | tide-admin/index.ts:366 |
getRealmPolicyById | GET /iga/role-policies/{id} | Find a policy by id. 404 resolves to null. | tide-admin/index.ts:385 |
upsertRealmPolicy | POST /iga/role-policies | Upsert a named policy. Reserved name tide-realm-admin returns 403. | tide-admin/index.ts:404 |
deleteRealmPolicyByName | DELETE /iga/role-policies/name/{name} | Delete a policy by name. | tide-admin/index.ts:425 |
deleteRealmNamedPolicyById | DELETE /iga/role-policies/{id} | Delete a policy by id. | tide-admin/index.ts:439 |
Legacy compat tide-admin paths (not in current source)
The following are the paths the api-client targets under /admin/realms/{realm}/tide-admin/.... No @Path for them exists in the current Java sources, so on the current build they return 404 and the api-client degrades to a no-op (empty/{}/zero). Documented from the api-client call site; consult tide-admin/index.ts for the request/response Zod schemas.
| Method | Path | Purpose (per api-client) | Source (api-client) |
|---|---|---|---|
| GET | /tide-admin/change-set/all/requests | List all change-set bundles (governance inbox). | tide-admin/index.ts:121, :130 |
| GET | /tide-admin/change-set/{id}/activity | Timeline (requestor + approval/comment events) for one bundle. 404 to empty timeline. | tide-admin/index.ts:148 |
| POST | /tide-admin/change-set/sign/batch | Operator approval entry point for a batch; Tide realms return requiresApprovalPopup + draft requests. | tide-admin/index.ts:169 |
| POST | /tide-admin/change-set/commit/batch | Finalize an approved batch. | tide-admin/index.ts:194 |
| POST | /tide-admin/change-set/cancel/batch | Drop a draft/pending batch. | tide-admin/index.ts:212 |
| GET | /tide-admin/change-set/{type}/requests | Legacy per-bucket (clients/users/roles/groups) pending-CR list. 404 to []. | tide-admin/index.ts:690 |
| POST | /tide-admin/change-set/sign | Legacy single-request sign ({changeSetId, changeSetType, actionType}). | tide-admin/index.ts:717 |
| POST | /tide-admin/change-set/commit | Legacy single-request commit (same body). | tide-admin/index.ts:736 |
| POST | /tide-admin/change-set/{id}/comments | Add a comment to a bundle. | tide-admin/index.ts:566 |
| PUT | /tide-admin/change-set/{id}/comments/{commentId} | Edit a comment (author-gated). | tide-admin/index.ts:594 |
| DELETE | /tide-admin/change-set/{id}/comments/{commentId} | Delete a comment (author-gated). | tide-admin/index.ts:622 |
| GET | /tide-admin/users/{id}/draft/status | User-level draft state {draftStatus, deleteStatus}. 404 to {}. | tide-admin/index.ts:246 |
| GET | /tide-admin/users/{userId}/roles/{roleId}/draft/status | Per-role draft state for a user role-mapping. 404 to {}. | tide-admin/index.ts:268 |
| GET | /tide-admin/composite/{parentId}/child/{childId}/draft/status | Draft state for a composite-role mapping. 404 to {}. | tide-admin/index.ts:290 |
| GET | /tide-admin/forseti-contracts | List Forseti contracts (compat surface). | tide-admin/index.ts:312 |
| PUT | /tide-admin/forseti-contracts | Upsert a Forseti contract (idempotent on body hash). | tide-admin/index.ts:326 |
| GET | /tide-admin/realm-policy | Current realm-policy state (status always present). 404 to {status:"none"}. | tide-admin/index.ts:472 |
| POST | /tide-admin/realm-policy/pending | Propose a new realm policy (template or raw contract code). | tide-admin/index.ts:489 |
| POST | /tide-admin/realm-policy/commit | Finalize an approved realm policy (409 below quorum). | tide-admin/index.ts:508 |
| POST | /tide-admin/realm-policy/request-delete | Propose deletion of an active realm policy. | tide-admin/index.ts:523 |
| POST | /tide-admin/realm-policy/commit-delete | Finalize an approved delete proposal. | tide-admin/index.ts:538 |
| DELETE | /tide-admin/realm-policy | Direct delete of a not-yet-committed (pending) policy. | tide-admin/index.ts:554 |
| GET | /tide-admin/server-cert/requests | List SPIFFE server-cert requests (PENDING/ACTIVE/DENIED). 404 to []. | tide-admin/index.ts:647 |
| POST | /tide-admin/server-cert/revoke | Revoke an issued server cert ({instanceId}). | tide-admin/index.ts:665 |
A separate tideAdminResources resource (provider distinct from tide-admin) serves one method the api-client calls under /admin/realms/{realm}/tideAdminResources/...:
| Method | Path | Purpose (per api-client) | Source (api-client) |
|---|---|---|---|
| POST | /tideAdminResources/get-required-action-link | Tide-aware analogue of users/{id}/execute-actions-email: returns the required-action link as text/plain instead of mailing it. Bookkeeping (userId, lifespan, redirect_uri, client_id) on the query string; body is a bare JSON array of required-action names. | tide-admin/index.ts:763 |
6. Ragnarok backup and offboarding
Mounted by the Ragnarok admin resource provider (provider id ragnarok) at /admin/realms/{realm}/ragnarok/.... Source: ragnarok/ragnarok-tidecloak-spi/src/main/java/org/ragnarok/RagnarokAdminResources/RagnarokAdminRealmResource.java. All require manage-realm. For the procedure, see the Ragnarok guide.
POST toggle-ragnarok
- Path:
POST /admin/realms/{realm}/ragnarok/toggle-ragnarok(RagnarokAdminRealmResource.java:193) - Purpose: Enables offboarding backup: runs the local
EnableOffboardVRK ceremony, stores the encrypted-VVK backup shard (eVVK) on the vendor-key component, enforces email-as-login prerequisites, and setsbackupOnon the Tide IdP. - Auth:
manage-realm. - Request (form):
isRagnarokEnabled(boolean; must betrue. Once enabled, backup cannot be disabled). - Response:
200text/plain("Offboarding backup has been enabled.");400ifisRagnarokEnabled=falseor the vendor-key component is absent. - Governed: No (ungoverned, local VRK sign, no quorum).
POST trigger-offboarding
- Path:
POST /admin/realms/{realm}/ragnarok/trigger-offboarding(RagnarokAdminRealmResource.java:104) - Purpose: Files an
OFFBOARD_REALMchange request (entity typeREALM) that, on commit, runs the destructive teardown (reconstruct VVK fromeVVK, make the realm self-custodial, remove the Tide IdP, restore password login). - Auth:
manage-realm. - Request: empty body.
- Response:
202 AcceptedwithLocationandX-Change-Request-Idheaders when the CR is filed.412 Precondition Failedtext/plainwhen a pre-flight gate fails: backup not enabled (eVVKmissing) or fewer activetide-realm-admins than the offboard threshold.409 Conflictif a pendingREALM-scoped CR already exists.
- Governed: Yes. Commit floor is
max(normal quorum, iga.offboardMinAdmins)(defaultoffboardMinAdmins = 3).
GET change-set/offboarding/requests
- Path:
GET /admin/realms/{realm}/ragnarok/change-set/offboarding/requests(RagnarokAdminRealmResource.java:91) - Purpose: Legacy listing of offboarding requests.
- Auth:
manage-realm. - Response:
200empty JSON array (no longer backed by a store; offboarding CRs surface through the standard IGA change-request listing). The SPA discovers a pending offboard by filteringGET /iga/change-requests?status=PENDINGfor anOFFBOARD_REALMCR (ragnarok/index.ts:158).
7. Public and console config
Pre-authentication resources under /realms/{realm}/.... No admin token. Source: tidecloak-idp-extensions/tidecloak-key-provider/src/main/java/org/tidecloak/... (PublicResource, TideConsoleResource).
GET public/get-tide-config
- Path:
GET /realms/{realm}/public/get-tide-config(PublicResource.java:28) - Purpose: Returns public, pre-auth Tide adapter configuration for a client:
vendorId,homeOrkUrl, per-originclient-origin-auth-<origin>signatures, and branding URLs. - Auth:
public. - Request:
clientId(query). - Response:
200JSON;404if the client is unknown;500on error.
GET tide-console/tidecloak.json
- Path:
GET /realms/{realm}/tide-console/tidecloak.json(TideConsoleResource.java:83) - Purpose: Origin-bound adapter install config the SPA fetches before login:
realm,auth-server-url,resource(tide-admin-console),public-client,client-origin-auth-<origin>entries,gVVK,vvkId,orkUrl,payerPublic, thresholdT/N, branding. - Auth:
public. - Response:
200JSON when the realm is Tide-enabled and the console origin is signed;404({}) otherwise. No-cache.
GET tide-console/bootstrap.json
- Path:
GET /realms/{realm}/tide-console/bootstrap.json(TideConsoleResource.java:93) - Purpose: Pre-auth bootstrap descriptor so the SPA can discover how to start OIDC login on an un-provisioned realm:
realm,auth-server-url,bootstrapRealm(master),bootstrapClientId(tide-console-bootstrap). Self-heals the bootstrap client if missing. - Auth:
public. - Response:
200JSON (always available, even on un-provisioned realms). No-cache.
Console SPA serving
| Method | Path | Purpose | Source |
|---|---|---|---|
| GET | /realms/{realm}/tide-console/ and /index.html | Serve the SPA shell (index.html) with window.__TIDE_BASE__ injected and /__TIDE_BASE__/ rewritten to the realm console base. No-cache. | TideConsoleResource.java:68, :73 |
| GET | /realms/{realm}/tide-console/assets/{path} | Serve hashed assets (immutable, 1-year cache). 400 if path contains ..; 404 if absent. | TideConsoleResource.java:103 |
| GET | /realms/{realm}/tide-console/{path} | Catch-all: serve embedded side files (e.g. silent-check-sso.html) or fall back to the SPA shell for client-side routes. | TideConsoleResource.java:115 |